﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TanmiaGrp.Basis.Core;
using RefactorThis.GraphDiff;
using RefactorThis.GraphDiff.Attributes;

namespace TanmiaGrp.GeneralLedger.Core
{
    /// <summary>
    /// Represents the account.
    /// </summary>
    /// <remarks>
    /// AccountNumber is indexed <see cref="GlDbContext.OnModelCreating"/>
    /// </remarks>
    public partial class Account : BusinessObject
    {
        /// <summary>
        /// Represent the header node in tree of account.
        /// </summary>
        public static readonly Account ChartOfAccountHeader = new Account { AccountName = "شجرة الحسابات", AccountID = -1, IsTransactional = false };

        #region Private Fields
        private int accountID;
        private string accountNumber;
        private string accountName;
        private int level;
        private bool isTransactional;
        private bool isEnabled;
        private bool isDebitable;
        private bool isCreditable;
        private bool isCheckable;
        private bool isCashable;
        private double? maxDebit;
        private double? maxCredit;
        private bool isFinal;
        private Account parent;
        private ObservableCollection<Group> accountGroups;
        private Account finalAccount;
        private DateTime? expiryDate;
        private DateTime? lastOperationDate;
        #endregion

        #region Keys
        /// <summary>
        /// represent account primary key.
        /// </summary>
        [Key, DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]
        public int AccountID
        {
            get { return accountID; }
            set
            {
                if (accountID != value)
                {
                    accountID = value;
                    RaisePropertyChanged(() => AccountID);
                }
            }
        }

        /// <summary>
        /// The parent of the current account.
        /// </summary>
        [AssociationType(AssociationTypeEnum.Association)]
        [Associated]
        public Account Parent
        {
            get { return parent; }
            set
            {
                if (parent != value)
                {
                    parent = value;
                    RaisePropertyChanged(() => Parent);
                }
            }
        }

        public int? ParentAccountID { get; set; }

        /// <summary>
        /// Final account.
        /// </summary>
        [Associated]
        public Account FinalAccount
        {
            get { return finalAccount; }
            set
            {
                if (finalAccount != value)
                {
                    finalAccount = value;
                    RaisePropertyChanged(() => FinalAccount);
                }
            }
        }

        public int? FinalAccountID { get; set; }

        #endregion

        /// <summary>
        /// Account number according the number system.
        /// </summary>
        [Required, MaxLength(64)]
        public string AccountNumber
        {
            get { return accountNumber; }
            set
            {
                if (accountNumber != value)
                {
                    accountNumber = value;
                    RaisePropertyChanged(() => AccountNumber);
                }
            }
        }

        /// <summary>
        /// Account name.
        /// </summary>
        [Required, MaxLength(128)]
        public string AccountName
        {
            get { return accountName; }
            set
            {
                if (accountName != value)
                {
                    accountName = value;
                    RaisePropertyChanged(() => AccountName);
                }
            }
        }

        /// <summary>
        /// Represents the level of the account on the account's tree.
        /// </summary>
        public int Level
        {
            get { return level; }
            set
            {
                if (level != value)
                {
                    level = value;
                    RaisePropertyChanged(() => Level);
                }
            }
        }

        /// <summary>
        /// If True, you can make transaction on that account. False.
        /// Just for arrangement you can not make transaction on the account.
        /// </summary>
        public bool IsTransactional
        {
            get { return isTransactional; }
            set
            {
                if (isTransactional != value)
                {
                    isTransactional = value;
                    RaisePropertyChanged(() => IsTransactional);
                }
            }
        }

        /// <summary>
        /// Determines if the account is enabled or disabled.
        /// </summary>
        public bool IsEnabled
        {
            get { return isEnabled; }
            set
            {
                isEnabled = value;
                RaisePropertyChanged(() => IsEnabled);
            }
        }

        /// <summary>
        /// هل يمكن لرصيد هذا الحساب أن يكون دائن
        /// Determines if current account accept debit movement or not.
        /// </summary>
        public bool IsDebitable
        {
            get { return isDebitable; }
            set
            {
                if (isDebitable != value)
                {
                    isDebitable = value;
                    RaisePropertyChanged(() => IsDebitable);
                }
            }
        }

        /// <summary>
        /// هل يمكن لرصيد هذا الحساب أن يكون مدين
        /// Determines if current account accept credit movement or not.
        /// </summary>
        public bool IsCreditable
        {
            get { return isCreditable; }
            set
            {
                if (isCreditable != value)
                {
                    isCreditable = value;
                    RaisePropertyChanged(() => IsCreditable);
                }
            }
        }

        /// <summary>
        /// Determines if current account accept check in its movement or not.
        /// </summary>
        public bool IsCheckable
        {
            get { return isCheckable; }
            set
            {
                if (isCheckable != value)
                {
                    isCheckable = value;
                    RaisePropertyChanged(() => IsCheckable);
                }
            }
        }

        /// <summary>
        /// Determines if current account just accept cash in its movement or not.
        /// </summary>
        public bool IsCashable
        {
            get { return isCashable; }
            set
            {
                if (isCashable != value)
                {
                    isCashable = value;
                    RaisePropertyChanged(() => IsCashable);
                }
            }
        }

        /// <summary>
        /// maximum limit on the debit.
        /// </summary>
        [Range(0, double.MaxValue)]
        public double? MaxDebit
        {
            get { return maxDebit; }
            set
            {
                if (maxDebit != value)
                {
                    maxDebit = value;
                    RaisePropertyChanged(() => MaxDebit);
                }
            }
        }

        /// <summary>
        /// maximum limit on the credit.
        /// </summary>
        [Range(0, double.MaxValue)]
        public double? MaxCredit
        {
            get { return maxCredit; }
            set
            {
                if (maxCredit != value)
                {
                    maxCredit = value;
                    RaisePropertyChanged(() => MaxCredit);
                }
            }
        }

        /// <summary>
        /// determines if the current account is final or not.
        /// </summary>
        public bool IsFinal
        {
            get { return isFinal; }
            set
            {
                if (isFinal != value)
                {
                    isFinal = value;
                    RaisePropertyChanged(() => IsFinal);
                }
            }
        }

        /// <summary>
        /// Groups of the account.
        /// </summary>
        [AssociationType(AssociationTypeEnum.Aggregation)]
        [Associated]
        public ObservableCollection<Group> AccountGroups
        {
            get { return accountGroups; }
            set
            {
                if (accountGroups != value)
                {
                    accountGroups = value;
                    RaisePropertyChanged(() => AccountGroups);
                }
            }
        }

        public virtual ICollection<Account> AccountChildren { get; set; }

        /// <summary>
        /// when expiry date is satisfied the account will be suspended.
        /// </summary>
        public DateTime? ExpiryDate
        {
            get { return expiryDate; }
            set
            {
                if (expiryDate != value)
                {
                    expiryDate = value;
                    RaisePropertyChanged(() => ExpiryDate);
                }
            }
        }

        /// <summary>
        /// Gets total debit of all movement of this account.
        /// </summary>
        public double TotalDebit { get; set; }

        /// <summary>
        /// Gets total credit of all movement of this account.
        /// </summary>
        public double TotalCredit { get; set; }

        /// <summary>
        /// Gets the balance of this account.
        /// Balance = TotalDebit - TotalCredit
        /// </summary>
        public double Balance { get; set; }

        /// <summary>
        /// Gets or sets the last Credit, or Debit operation date has been done on current Account.
        /// This value is updated on Posting or Unposting the Voucher.
        /// </summary>
        public DateTime? LastOperationDate
        {
            get { return lastOperationDate; }
            set
            {
                if (lastOperationDate != value)
                {
                    lastOperationDate = value;
                    RaisePropertyChanged(() => LastOperationDate);
                }
            }
        }

        #region Overridded Methods

        public override bool Equals(object obj)
        {
            Account paramAccount = obj as Account;

            if (!base.Equals(obj)) return false;
            if (paramAccount == null) return false;

            if (!AccountID.Equals(paramAccount.AccountID)) return false;
            if (!object.Equals(AccountNumber, paramAccount.AccountNumber)) return false;
            if (!object.Equals(AccountName, paramAccount.AccountName)) return false;

            return true;

        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        public override string ToString()
        {
            return string.Format("{0}-{1}", AccountNumber, AccountName);
        }

        #endregion
    }
}
